/**
 * 项目整体视图脚本
 * 1. 全局 loading 雪花图
 * 2. 针对项目重新封装后的弹窗
 * 3. 模板文件的渲染
 * 4. 封装 ajax
 */

(function(layui) {
  'use strict';

  var MODULE_NAME = 'view';

  var ClassName = {
    LOADING_PARENT_RELATIVE: 'layadmin-loading-parent-relative',
    LOADING_PARENT_HIDDEN  : 'layadmin-loading-parent-hidden'
  };

  layui.define(['jquery', 'layer', 'laytpl', 'setter'], function(exports) {
    var $ = layui.jquery;
    var layer = layui.layer;
    var laytpl = layui.laytpl;
    var setter = layui.setter;
    var hint = layui.hint();

    var Tpl = function(id) {
      this.id = id;
      this.container = $(id || setter.app);
    };

    var view = function(selector) {
      return new Tpl(selector);
    };

    // 加载
    view.loading = function(elem) {
      this.$elem = $(elem);
      var position = this.$elem.css('position');
      if (!position || position === 'static') {
        this.$elem
          .addClass(ClassName.LOADING_PARENT_RELATIVE)
          .addClass(ClassName.LOADING_PARENT_HIDDEN);
      }

      $(elem).append(
        this.$load = $('<i class="layui-anim layui-anim-rotate layui-anim-loop layui-icon layui-icon-loading layadmin-loading"></i>')
      );
    };

    // 移除加载
    view.removeLoading = function() {
      this.$load && this.$load.remove();
      this.$elem && this.$elem
        .removeClass(ClassName.LOADING_PARENT_HIDDEN)
        .removeClass(ClassName.LOADING_PARENT_RELATIVE);
    };

    // 退出 code from layuiAdmin
    view.exit = function(callback) {
      // 清空本地记录的 token
      layui.data(setter.storageName, {
        key   : setter.request.tokenName,
        remove: true
      });

      // 跳转到登入页
      $.isFunction(callback) && callback();
    };

    // Ajax请求 code from layuiAdmin
    view.req = function(options) {
      var successHandler = options.success;
      var errorHandler = options.error;
      var request = setter.request;
      var response = setter.response;
      var debug = function() {
        return setter.debug
          ? '<br><cite>URL：</cite>' + options.url
          : '';
      };

      options.data = options.data || {};
      options.headers = options.headers || {};

      if (request.tokenName) {
        var sendData = typeof options.data === 'string'
          ? JSON.parse(options.data)
          : options.data;

        // 自动给参数传入默认 token
        options.data[request.tokenName] = request.tokenName in sendData
          ? options.data[request.tokenName]
          : (layui.data(setter.storageName)[request.tokenName] || '');

        // 自动给 Request Headers 传入 token
        options.headers[request.tokenName] = request.tokenName in options.headers
          ? options.headers[request.tokenName]
          : (layui.data(setter.storageName)[request.tokenName] || '');
      }

      delete options.success;
      delete options.error;

      return $.ajax($.extend({
        type    : 'get',
        dataType: 'json',
        success : function(res) {
          var statusCode = response.statusCode;

          // 只有 response 的 code 一切正常才执行 done
          if (res[response.statusName] === statusCode.ok) {
            typeof options.done === 'function' && options.done(res);
            // 登录状态失效，清除本地 access_token，并强制跳转到登入页
          } else if (res[response.statusName] === statusCode.logout) {
            view.exit();
          } else { // 其它异常
            var error = [
              '<cite>Error：</cite> ' + (res[response.msgName] || '返回状态码异常'),
              debug()
            ].join('');
            view.error(error);
          }

          // 只要 http 状态码正常，无论 response 的 code 是否正常都执行 success
          $.isFunction(successHandler) && successHandler(res);
        },
        error: function(e, code) {
          var error = [
            '请求异常，请重试<br><cite>错误信息：</cite>' + code,
            debug()
          ].join('');
          view.error(error);

          $.isFunction(errorHandler) && errorHandler(e);
        }
      }, options));
    };

    // 弹窗，用于页面展示
    view.popup = function(options) {
      var hasProp       = function(prop) { return $.isPlainObject(options) && (prop in options) };
      var hasPropTitle  = hasProp('title');
      var hasPropAnim   = hasProp('anim');
      var hasPropMaxMin = hasProp('maxmin');
      var hasCloseBtn   = hasProp('closeBtn');
      var optCloseBtn   = hasCloseBtn ? options.closeBtn : true;
      var optSuccess    = options.success;
      var optSkin       = options.skin;
      var optTitle      = hasPropTitle ? options.title : '提示';
      var optAnim       = hasPropAnim ? options.anim : -10;
      var optMaxmin     = hasPropMaxMin ? options.maxmin : (hasPropTitle && Boolean(optTitle));
      var optRestore    = options.restore;
      var optFull       = options.full;

      // 注入页面放大放小的补丁
      var handlerFullPatch = function($layer) {
        if (this.type === 1 && optMaxmin === true) {
          var $container  = $layer.children('.layui-layer-content');
          var isFull      = $layer.find('.layui-layer-setwin>.layui-layer-maxmin')[0];
          $container.height(isFull ? ($container.height() - 20) : 'auto');
        }
      };

      delete options.maxmin;
      delete options.closeBtn;
      delete options.success;
      delete options.skin;
      delete options.title;
      delete options.anim;
      delete options.min;
      delete options.restore;
      delete options.full;

      return layer.open($.extend({
        maxmin    : optMaxmin,
        anim      : optAnim,
        move      : false,
        type      : 1,
        title     : $.type(optTitle) === 'string' ? optTitle : !!options.title,
        content   : $.type(options) === 'string' ? options : '',
        id        : 'layadmin-view-popup',
        skin      : 'layadmin-view-popup' + (optAnim > -10 ? '' : ' layui-anim layui-anim-SlideDown') + (optSkin ? ' ' + optSkin : ''),
        shadeClose: true,
        closeBtn  : false,
        offset    : 't',
        area      : '290px',
        resize    : true,
        full      : function(layero) {
          var area = layero.attr('area');
          var areas = area && area.split(',');
          if (areas && areas.length > 2) {
            areas[2] = (+areas[2]) - 40;
            layero.attr('layadmin-area', areas.join(','));
            layero.attr('area', areas.join(','));
          }

          layero.css('margin-top', '-40px');
          layero.css('border-radius', 0);
          handlerFullPatch.call(this, layero);
          $.isFunction(optFull) && optFull(layero);
        },
        restore: function(layero) {
          layero.css('margin-top', '');
          layero.css('border-radius', '');
          handlerFullPatch.call(this, layero);
          $.isFunction(optRestore) && optRestore(layero);
        },
        success: function(layero, index) {
          if (optTitle && optCloseBtn) {
            var $btnClose = $('<i class="layui-icon" close>&#x1006;</i>');
            layero.append($btnClose);
            $btnClose.on('click', function() {
              layer.close(index);
            });
          }

          if (optMaxmin && !options.move) {
            var $setwin = layero.find('.layui-layer-setwin');
            var $max = $setwin.children('.layui-layer-max');
            var $title = layero.find('.layui-layer-title');
            $setwin.children('.layui-layer-min').remove(); // layer 最小化仍有问题，移除这个参数的配置
            $max.hide();
            $title.dblclick(function() {
              $max.trigger('click');
            });
          }

          $.isFunction(optSuccess) && optSuccess.apply(this, arguments);
        }
      }, $.type(options) === 'string' ? {} : options));
    };

    // 弹窗，用于询问
    view.confirm = function(options) {
      var optSuccess = options.success;
      var optBtn = $.isPlainObject(options) && ('btn' in options) ? options.btn : ['确认', '取消'];
      var optYes = options.yes;
      var optCancel = options.cancel;

      delete options.success;
      delete options.btn;
      delete options.yes;
      delete options.cancel;

      optBtn = $.isArray(optBtn) ? optBtn : [optBtn];

      return view.popup($.extend({
        id        : 'layadmin-view-popup-confirm',
        content   : $.type(options) === 'string' ? options : '',
        resize    : false,
        move      : false,
        shadeClose: false,
        maxmin    : false,
        success   : function(layero, index) {
          var $btn;
          var $container = $('<div class="layadmin-view-popup-btn-container"></div>');
          var i = 0;
          var len = optBtn.length;
          var othis = this;

          var createHandler = function(fn) {
            return function() {
              if ($.isFunction(fn)) {
                var ret = fn.apply(othis, [index, layero]);
                ret === false ? '' : layer.close(index);
              } else {
                layer.close(index);
              }
            };
          };

          for (;i < len; i++) {
            if (i === 0) {
              $btn = $('<button class="layui-btn layadmin-btn-confirm">' + optBtn[i] + '</button>');
              $btn.on('click', createHandler(optYes));
            } else if (i === 1) {
              $btn = $('<button class="layui-btn layadmin-btn-cancel">' + optBtn[i] + '</button>');
              $btn.on('click', createHandler(optCancel));
            } else {
              $btn = $('<button class="layui-btn layui-layer-btn' + i + '">' + optBtn[i] + '</button>');
              $btn.on('click', createHandler(options['btn' + (i + 1)]));
            }
            $container.append($btn);
          }
          len && layero.append($container);

          $.isFunction(optSuccess) && optSuccess.apply(this, arguments);
        }
      }, $.type(options) === 'string' ? {} : options));
    };

    // 弹窗，用于警示
    view.alert = function(options, type) {
      var types = {
        success: '<i class="layui-icon layui-icon-ok"></i>',
        error  : '<i class="layui-icon layui-icon-close"></i>',
        info   : 'i',
        warning: '!'
      };

      var optSuccess = options.success;
      var optBtn = $.isPlainObject(options) && ('btn' in options) ? options.btn : (function() {
        return type === 'error' || type === 'warning' ? ['确认', '取消'] : ['确认'];
      }());

      delete options.success;
      delete options.btn;

      return view.confirm($.extend({
        id     : 'layadmin-view-popup-alert',
        btn    : optBtn,
        content: $.type(options) === 'string' ? options : '',
        success: function(layero) {
          if (type) {
            var $icon = $('<span class="layadmin-view-popup-status is-' + type + '">' + types[type] + '</span>');
            layero.find('#' + this.id).addClass('layadmin-layer-content').prepend($icon);
          }
          $.isFunction(optSuccess) && optSuccess.apply(this, arguments);
        }
      }, $.type(options) === 'string' ? {} : options));
    };

    // 弹窗，用于错误
    view.error = function(message) {
      return view.alert({
        id        : 'layadmin-view-popup-error',
        content   : message,
        title     : false,
        btn       : [],
        maxWidth  : 300,
        shadeClose: true,
        anim      : 6,
        maxmin    : false
      }, 'warning');
    };

    // 弹窗，用于广播
    view.broadcast = function(options) {
      var optTitle = options.title || '通知';

      delete options.title;
      delete options.resize;
      delete options.maxmin;
      delete options.move;
      delete options.anim;

      return view.popup($.extend({
        title  : optTitle,
        id     : 'layadmin-view-popup-broadcast',
        content: $.type(options) === 'string' ? options : '',
        anim   : -2,
        skin   : 'layadmin-view-popup-broadcast layui-anim layui-anim-Broadcast',
        offset : 'rb',
        shade  : false,
        resize : false,
        move   : false,
        time   : 5 * 1000
      }, $.type(options) === 'string' ? {} : options));
    };

    // 弹窗，抽屉
    view.popupRight = function(options) {
      delete options.offset;
      delete options.anim;
      delete options.move;
      delete options.resize;

      return view.popup($.extend({
        id      : 'layadmin-view-popup-popupright',
        content : $.type(options) === 'string' ? options : '',
        closeBtn: false,
        title   : false,
        area    : '300px',
        anim    : -2,
        offset  : 'r',
        skin    : 'layadmin-view-popup-popupright layui-anim layui-anim-PopupRight',
        resize  : false,
        move    : false
      }, options));
    };

    /**
     * Code from layuiAdmin
     */
    Tpl.prototype.render = function render(path, params) {
      var that = this;

      path = setter.views + (path.slice(0, 1) === '/' ? path.slice(1) : path) + setter.engine;
      view.loading(that.container);

      $.ajax({
        url     : path,
        type    : 'get',
        dataType: 'html',
        data    : {
          v: layui.cache.version
        },
        success: function(html) {
          html = '<div>' + html + '</div>';

          var $title = $(html).find('title');
          var title = $title.text() || (html.match(/\<title\>([\s\S]*)\<\/title>/) || [])[1];

          var res = {
            title: title,
            body : html
          };

          that.params = params || {};

          if (that.then) {
            that.then(res);
            delete that.then;
          }

          that.parse(html);
          view.removeLoading();

          if (that.done) {
            that.done(res);
            delete that.done;
          }
        },
        error: function(e) {
          view.removeLoading();

          if (that.render.isError) {
            return view.error('请求视图文件异常，状态：' + e.status);
          }

          if (e.status === 404) {
            that.render('template/tips/404');
          } else {
            that.render('template/tips/error');
          }

          that.render.isError = true;
        }
      });

      return that;
    };

    // 解析模板
    Tpl.prototype.parse = function(html, refresh, callback) {
      var that = this;
      var isScriptTpl = typeof html === 'object'; // 是否模板元素
      var elem = isScriptTpl ? html : $(html);
      var elemTemp = isScriptTpl ? html : elem.find('*[template]');

      var fn = function(options) {
        var tpl = laytpl(options.dataElem.html());

        var res = $.extend({
          params: router.params
        }, options.res);

        options.dataElem.after(tpl.render(res));
        typeof callback === 'function' && callback();

        try {
          options.done && new Function('d', options.done)(res);
        } catch (e) {
          console.error(options.dataElem[0], '\n存在错误回调脚本\n\n', e);
        }
      };

      var router = layui.router();

      elem.find('title').remove();
      that.container[refresh ? 'after' : 'html'](elem.children());

      router.params = that.params || {};

      // 遍历模板区块
      for (var i = elemTemp.length; i > 0; i--) {
        (function() {
          var dataElem = elemTemp.eq(i - 1);
          var layDone = dataElem.attr('lay-done') || dataElem.attr('lay-then'); // 获取回调
          var url = laytpl(dataElem.attr('lay-url') || '').render(router);// 接口 url
          var data = laytpl(dataElem.attr('lay-data') || '').render(router);// 接口参数
          var headers = laytpl(dataElem.attr('lay-headers') || '').render(router); // 接口请求的头信息

          try {
            data = new Function('return ' + data + ';')();
          } catch (e) {
            hint.error('lay-data: ' + e.message);
            data = {};
          }

          try {
            headers = new Function('return ' + headers + ';')();
          } catch (e) {
            hint.error('lay-headers: ' + e.message);
            headers = headers || {};
          }

          if (url) {
            view.req({
              type    : dataElem.attr('lay-type') || 'get',
              url     : url,
              data    : data,
              dataType: 'json',
              headers : headers,
              success : function(res) {
                fn({
                  dataElem: dataElem,
                  res     : res,
                  done    : layDone
                });
              }
            });
          } else {
            fn({
              dataElem: dataElem,
              done    : layDone
            });
          }
        }());
      }

      return that;
    };

    // 自动渲染数据模板
    Tpl.prototype.autoRender = function(id, callback) {
      var that = this;
      $(id || 'body').find('*[template]').each(function(index, item) {
        var othis = $(this);
        that.container = othis;
        that.parse(othis, 'refresh');
      });
    };

    // 直接渲染字符
    Tpl.prototype.send = function(views, data) {
      var tpl = laytpl(views || this.container.html()).render(data || {});
      this.container.html(tpl);
      return this;
    };

    // 刷新试图
    Tpl.prototype.refresh = function(callback) {
      var that = this;
      var next = that.container.next();
      var templateid = next.attr('lay-templateid');

      // eslint-disable-next-line eqeqeq
      if (that.id != templateid) return that;

      that.parse(that.container, 'refresh', function() {
        that.$container.siblings('[lay-templateid="' + that.id + '"]:last').remove();
        typeof callback === 'function' && callback();
      });

      return that;
    };

    // 请求成功后回调
    Tpl.prototype.then = function then(callback) {
      this.then = callback;
      return this;
    };

    // 渲染完后回调
    Tpl.prototype.done = function done(callback) {
      this.done = callback;
      return this;
    };

    exports(MODULE_NAME, view);
  });
})(layui);
